<template>
  <div class="date-picker bg-surface">
    <div class="select gap-row-16">
      <div class="year pointer" :class="{ disable: monthSelectOpen }" @click="toggleYearSelect">{{ yearSelect }}</div>
      <div class="month pointer" :class="{ disable: yearSelectOpen }" @click="toggleMonthSelect">
        {{ monthOptions[monthSelect] }}
      </div>
    </div>
    <transition name="scale">
      <div
        v-show="yearSelectOpen || monthSelectOpen"
        class="select-options bg-surface"
        :class="selectOptionsHeightClass"
      >
        <div v-if="yearSelectOpen" class="year">
          <div
            v-for="year in yearOptions"
            class="option pointer"
            :class="{ 'bg-background': year === yearSelect }"
            :key="year"
            @click="setYear(year)"
          >
            <div class="check"><Icon v-if="year === yearSelect" size="16" name="check"></Icon></div>
            <div class="value">{{ year }}</div>
          </div>
        </div>
        <div v-if="monthSelectOpen" class="month">
          <div
            v-for="(monthText, monthValue) in monthOptions"
            class="option pointer"
            :class="{ 'bg-background': monthValue === monthSelect }"
            :key="monthValue"
            @click="setMonth(monthValue)"
          >
            <div class="check">
              <Icon v-if="monthValue === monthSelect" size="16" name="check"></Icon>
            </div>
            <div class="value">{{ monthText }}</div>
          </div>
        </div>
      </div>
    </transition>
    <div class="day-label">
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">S</div>
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">M</div>
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">T</div>
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">W</div>
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">T</div>
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">F</div>
      <div class="cell font-size-16 font-weight-400 line-height-140 text-input">S</div>
    </div>
    <div class="date-table">
      <div
        v-for="(date, i) in dateGrid"
        class="cell pointer font-size-14 font-weight-500 line-height-140"
        :class="{ selected: date.selected, 'text-input': date.year !== yearSelect || date.month !== monthSelect }"
        :key="i"
        @click="pickDate(date)"
      >
        {{ date.date }}
      </div>
    </div>
    <div v-if="includeTime" class="time-input gap-col-16">
      <div class="divider"></div>
      <div class="input bg-background gap-row-8" :class="{ error: !!timeErr }">
        <input
          v-model="time"
          type="text"
          class="font-size-14 font-weight-500 line-height-140"
          @click="onTimeFocus"
          @blur="onTimeBlur"
        />
        <Icon class="clock" size="14" name="clock"></Icon>
      </div>
      <div v-if="timeErr" class="err-msg font-size-12 font-weight-400 line-height-120 text-primary-x">
        {{ timeErr }}
      </div>
    </div>
    <div class="btns gap-row-8">
      <div class="cancel pointer font-size-14 font-weight-500 line-height-140" @click="cancel">Cancel</div>
      <div class="confirm pointer font-size-14 font-weight-500 line-height-140 text-primary" @click="confirm">OK</div>
    </div>
  </div>
</template>

<script setup>
import { padStart } from 'lodash-es'
import { computed, ref } from 'vue'
import { $t } from '@/i18n'

const props = defineProps({
  modelValue: { type: Date, required: true },
  includeTime: { type: Boolean, default: false },
})
const emits = defineEmits(['update:modelValue', 'close'])

const datePicked = ref(props.modelValue)
const yearSelect = ref(datePicked.value.getFullYear())
const monthSelect = ref(datePicked.value.getMonth())
function setYear(year) {
  yearSelect.value = year
  yearSelectOpen.value = false
}
function setMonth(month) {
  if (month >= 0 && month <= 11) {
    monthSelect.value = month
    monthSelectOpen.value = false
  } else {
    throw new Error(`month value '${month}' incorrectly...`)
  }
}
const yearOptions = ref(genYearOptions())
const monthOptions = ref([
  $t('month.Jan'),
  $t('month.Feb'),
  $t('month.Mar'),
  $t('month.Apr'),
  $t('month.May'),
  $t('month.Jun'),
  $t('month.Jul'),
  $t('month.Aug'),
  $t('month.Sep'),
  $t('month.Oct'),
  $t('month.Nov'),
  $t('month.Dec'),
])
const yearSelectOpen = ref(false)
const monthSelectOpen = ref(false)
function toggleYearSelect() {
  yearSelectOpen.value = !yearSelectOpen.value
  if (yearSelectOpen.value && monthSelectOpen.value) {
    monthSelectOpen.value = false
  }
}
function toggleMonthSelect() {
  monthSelectOpen.value = !monthSelectOpen.value
  if (monthSelectOpen.value && yearSelectOpen.value) {
    yearSelectOpen.value = false
  }
}

const dateGrid = computed(() => {
  const year = yearSelect.value
  const month = monthSelect.value
  const date = datePicked.value
  const firstDate = new Date(year, month, 1)
  const firstDateDay = firstDate.getDay()
  const r = []
  for (let i = 0, j = 0; i < firstDateDay; i++, j--) {
    const date = new Date(year, month, j)
    const [y, m, d] = [date.getFullYear(), date.getMonth(), date.getDate()]
    r.unshift({ year: y, month: m, date: d })
  }
  const lastDate = new Date(year, month + 1, 0)
  const lastDateDate = lastDate.getDate()
  for (let currDate = 1; currDate <= lastDateDate; currDate++) {
    r.push({
      year,
      month,
      date: currDate,
      selected: year === date.getFullYear() && month === date.getMonth() && currDate === date.getDate(),
    })
  }
  const lastDateDay = lastDate.getDay()
  for (let i = 0; i < 6 - lastDateDay; i++) {
    const date = new Date(year, month, lastDateDate + i + 1)
    const [y, m, d] = [date.getFullYear(), date.getMonth(), date.getDate()]
    r.push({ year: y, month: m, date: d })
  }

  return r
})
const selectOptionsHeightClass = computed(() => {
  // 一週7天 * 6行
  if (dateGrid.value.length === 7 * 6) {
    return 'row-6'
  }
  return 'row-5'
})

// 還是先別幫他跳，突然滑動很干擾
// const monthOptionsEle = ref(null)
// watch(
//   monthSelectOpen,
//   (v) => {
//     if (v) {
//       if (monthSelect.value > 5) {
//         setTimeout(() => {
//           monthOptionsEle.value.scrollTo({
//             top: 1000,
//             behavior: 'smooth',
//           })
//         }, 160)
//       }
//     }
//   },
//   { immediate: true },
// )

const timeErr = ref('')
const time = computed(() => {
  const h = datePicked.value.getHours()
  const m = datePicked.value.getMinutes()
  return padStart(h, 2, '0') + ' : ' + padStart(m, 2, '0')
})
function onTimeFocus(evt) {
  const input = evt.target
  const selectionIndex = input.selectionStart
  if (selectionIndex < 4) {
    input.setSelectionRange(0, 2)
  } else {
    input.setSelectionRange(5, 7)
  }
}
function onTimeBlur(evt) {
  const input = evt.target
  const originValue = time.value

  const [hours, minutes] = input.value.split(':')
  const hoursTrim = hours?.trim()
  const minutesTrim = minutes?.trim()
  const hoursInt = parseInt(hoursTrim)
  const minutesInt = parseInt(minutesTrim)

  if (/^[0-9]{1,2}$/.test(hoursTrim) && hoursInt < 24 && /^[0-9]{1,2}$/.test(minutesTrim) && minutesInt < 60) {
    const y = datePicked.value.getFullYear()
    const m = datePicked.value.getMonth()
    const d = datePicked.value.getDate()
    const seconds = datePicked.value.getSeconds()
    const ms = datePicked.value.getMilliseconds()
    datePicked.value = new Date(y, m, d, hoursInt, minutesInt, seconds, ms)
    timeErr.value = ''
  } else {
    input.value = originValue
    timeErr.value = $t('yup.time.format')
  }
}

function pickDate(date) {
  yearSelect.value = date.year
  monthSelect.value = date.month

  const hours = datePicked.value.getHours()
  const minutes = datePicked.value.getMinutes()
  const seconds = datePicked.value.getSeconds()
  const ms = datePicked.value.getMilliseconds()
  datePicked.value = new Date(date.year, date.month, date.date, hours, minutes, seconds, ms)
}

function cancel() {
  emits('close')
}

function confirm() {
  if (!timeErr.value) {
    emits('update:modelValue', datePicked.value)
    emits('close')
  }
}

function genYearOptions() {
  const currentYear = new Date().getFullYear()
  const startYear = 2023
  const yearArray = []

  for (let year = startYear; year <= currentYear; year++) {
    yearArray.push(year)
  }

  return yearArray
}
</script>

<style lang="scss" scoped>
.scale-enter-active,
.scale-leave-active {
  transition: transform 0.15s ease;
  transform-origin: top;
}

.scale-enter-from,
.scale-leave-to {
  transform: scaleY(0);
}

.date-picker {
  width: calc(2.5rem * 7 + 2rem);
  padding: 0.5rem 1rem;
  border-radius: 1rem;
  position: relative;

  .select {
    height: 2.75rem;
    padding: 0.5rem 1rem 0;
    display: flex;

    .year,
    .month {
      width: 50%;
      padding: 0.5rem 0 0.5rem 1.375rem;

      &::after {
        content: '';
        width: 0;
        height: 0;
        border-style: solid;
        border-width: 0.25rem 0.25rem 0 0.25rem;
        border-color: #ffffff transparent transparent transparent;
        position: relative;
        top: 50%;
        left: 1rem;
      }

      &.disable {
        color: rgb(var(--v-theme-input));
        &::after {
          display: none;
        }
      }
    }
  }

  .select-options {
    width: 100%;
    position: absolute;
    top: 3.25rem;
    left: 0rem;
    overflow: auto;

    &.row-5 {
      height: 18.5rem;
    }
    &.row-6 {
      height: 21rem;
    }

    .year,
    .month {
      .option {
        padding: 0.625rem 1rem;
        display: flex;
        .check {
          width: 2.375rem;
        }
      }
    }

    // .month {
    //   .option {
    //     padding: 0.625rem 0 0.625rem calc(50% - 0.5rem);
    //   }
    // }
  }

  .day-label,
  .date-table {
    display: grid;
    grid-template-columns: repeat(7, 1fr);

    .cell {
      width: 2.5rem;
      height: 2.5rem;
      padding: 0.625rem;
      text-align: center;
      &.selected {
        background: rgb(var(--v-theme-primary));
        border-radius: 1.25rem;
      }
    }
  }

  .time-input {
    .divider {
      height: 1px;
      background-color: rgba(255, 255, 255, 0.27);
    }

    .input {
      display: flex;
      align-items: center;
      padding: 0 0.5rem 0 0.75rem;
      margin-bottom: 0.3125rem;
      border-radius: 0.625rem;

      &.error {
        border: 0.0625rem solid rgb(var(--v-theme-primary-x));
        background: linear-gradient(0deg, rgba(235, 0, 27, 0.1) 0%, rgba(235, 0, 27, 0.1) 100%),
          rgb(var(--v-theme-background));
      }

      input {
        flex-grow: 1;
        width: 100%;
        height: 1.875rem;
        border-radius: 0.625rem;
        outline: none;
        letter-spacing: 0.2px;
      }
    }
  }

  .btns {
    height: 3.5rem;
    padding: 0.5rem 0.75rem;
    display: flex;
    justify-content: flex-end;

    .confirm,
    .cancel {
      padding: 0.625rem 0.75rem;
    }
  }
}
</style>
